#!/usr/local/bin/ruby

require 'optparse'

def daemonize
  exit if fork                   # Parent exits, child continues.
  Process.setsid                 # Become session leader.
  exit if fork                   # Zap session leader. See [1].
  Dir.chdir "/"                  # Release old working directory.
  File.umask 0000                # Ensure sensible umask. Adjust as needed.
  STDIN.reopen "/dev/null"       # Free file descriptors and
  STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
  STDERR.reopen STDOUT           # STDOUT/ERR should better go to a logfile.
end

OPTIONS = {
  :high_interval => 5.0,
  :low_interval  => 0.5,
  :command  => File.expand_path(File.dirname(__FILE__) + '/spawner'),
  :daemon   => false
}

ARGV.options do |opts|
  opts.banner = "Usage: spinner [options]"

  opts.separator ""

  opts.on <<-EOF
  Description:
    The spinner is a protection loop for the spawner, which will attempt to restart any FCGI processes
    that might have been restarted or outright crashed. It's a brute-force attempt that'll just try
    to run the spawner every X number of seconds, so it does pose a light load on the server.

  Examples:
    spinner # attempts to run the spawner with default settings every second with output on the terminal
    spinner -i 3 -d # only run the spawner every 3 seconds and detach from the terminal to become a daemon
    spinner -c '/path/to/app/script/process/spawner -p 9000 -i 10' -d # using custom spawner
  EOF

  opts.on("  Options:")

  opts.on("-c", "--command=path",    String)      { |OPTIONS[:command]| }
  opts.on("-h", "--high-interval=seconds", Float) { |OPTIONS[:high_interval]| }
  opts.on("-l", "--low-interval=seconds", Float)  { |OPTIONS[:low_interval]| }
  opts.on("-d", "--daemon")                       { |OPTIONS[:daemon]| }

  opts.separator ""

  opts.on("-h", "--help", "Show this help message.") { puts opts; exit }

  opts.parse!
end

daemonize if OPTIONS[:daemon]

trap(OPTIONS[:daemon] ? "TERM" : "INT") { exit }
trap("USR1") do
  $interval = ($interval == OPTIONS[:high_interval] ? OPTIONS[:low_interval] : OPTIONS[:high_interval])
  puts "New interval: #{$interval}"
end

$interval = OPTIONS[:high_interval]

loop do
  system(OPTIONS[:command])
  sleep($interval)
end